//
//  DerbyBoutState.m
//  DerbyOSSampler
//
//  Created by glenn andreas on 2/25/14.
//  Copyright (c) 2014 glenn andreas. All rights reserved.
//

#import "DerbyBoutState.h"
#import "DXMLObject.h"
#import "DXMLBoutObject.h"
#import "DXMLJamObject.h"
#import "DXMLPeriodObject.h"
#import "DXMLTeamObject.h"
#import "DXMLTimeStamp.h"
#import "DXMLSkaterEventObject.h"
#import "DerbyStopwatch.h"
#import "DXMLTimeOutObject.h"

@interface DXMLObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state;
- (void) updateBoutState: (DerbyBoutState *) state team: (DerbyBoutTeamState *) team;
@end

@implementation DerbyBoutState

+ (NSSet *) keyPathsForValuesAffectingShouldIntermissionClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldJamClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldLineupClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldOfficialFinalShow { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldOvertimeShow { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldPeriodClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldTimeoutClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldTimeToDerbuClockRun { return [NSSet setWithObject:@"state"]; }
+ (NSSet *) keyPathsForValuesAffectingShouldUnofficialFinalShow { return [NSSet setWithObject:@"state"]; }


- (BOOL) shouldPeriodClockRun { return DerbyBoutStateShouldPeriodClockRun(self.boutState); }
- (BOOL) shouldTimeoutClockRun { return DerbyBoutStateShouldTimeoutClockRun(self.boutState); }
- (BOOL) shouldJamClockRun { return DerbyBoutStateShouldJamClockRun(self.boutState); }
- (BOOL) shouldIntermissionClockRun { return DerbyBoutStateShouldIntermissionClockRun(self.boutState); }
- (BOOL) shouldTimeToDerbuClockRun { return DerbyBoutStateShouldTimeToDerbuClockRun(self.boutState); }
- (BOOL) shouldUnofficialFinalShow { return DerbyBoutStateShouldUnofficialFinalShow(self.boutState); }
- (BOOL) shouldOfficialFinalShow { return DerbyBoutStateShouldOfficialFinalShow(self.boutState); }
- (BOOL) shouldOvertimeShow { return DerbyBoutStateShouldOvertimeShow(self.boutState); }
- (BOOL) shouldLineupClockRun { return DerbyBoutStateShouldLineupClockRun(self.boutState); }

- (id) initWithBout:(DXMLBoutObject *)derbyXML
{
    self = [super init];
    if (self) {
        _derbyXML = derbyXML;
        _homeTeamState = [[DerbyBoutTeamState alloc] initWithTeam: derbyXML.homeTeam];
        _awayTeamState = [[DerbyBoutTeamState alloc] initWithTeam: derbyXML.awayTeam];
    }
    return self;
}
- (id) copyWithZone:(NSZone *)zone
{
    DerbyBoutState *retval = [[DerbyBoutState alloc] initWithBout:self.derbyXML];
    retval.boutState = self.boutState;
    retval.homeTeamState = [self.homeTeamState copyWithZone:zone];
    retval.awayTeamState = [self.awayTeamState copyWithZone:zone];
    // copy more state: todo
    return retval;
}
- (DXMLPeriodObject *) updatePeriods
{
    NSArray *periods = self.derbyXML.periods;
    DXMLPeriodObject *active = nil;
    NSInteger pnum = 0;
    for (DXMLPeriodObject *period in periods) {
        if (period.timestamp == nil) {
            continue; // not the current one, no time stamp yet
        }
        pnum += 1;
        active = period;
    }
    if (active == nil) {
        self.periodNumber = 1;
        self.activePeriod = nil;
        self.currentPeriod = periods[0];
        return periods[0];
    } else {
        self.activePeriod = active;
        self.currentPeriod = active;
        self.periodNumber = pnum;
        return active;
    }
}
- (DXMLJamObject *) updateJam
{
    DXMLPeriodObject *period = [self updatePeriods];
    NSInteger jnum = 0;
    DXMLJamObject *active = nil;
    DXMLJamObject *current = nil;
    NSInteger activeNum = 0;
    NSInteger currentNum = 0;
    for (DXMLJamObject *jam in period.children) {
        if ([jam.elementName isEqualToString:@"jam"]) {
            jnum += 1;
            current = jam;
            currentNum = jnum;
            if (jam.timestamp != nil) {
                active = jam;
                activeNum = jnum;
            } else {
                active = nil;
            }
        }
    }
    if (active) {
        self.jamNumber = activeNum;
        self.activeJam = active;
        self.currentJam = active;
        return active;
    } else if (current) {
        self.jamNumber = currentNum;
        self.activeJam = nil;
        self.currentJam = current;
        return current;
    } else {
        self.jamNumber = 1;
        self.activeJam = nil;
        self.currentJam = nil;
        return nil;
    }
}
- (DXMLObject *) updateTimeout
{
    DXMLPeriodObject *period = [self updatePeriods];
    __block DXMLObject *retval = nil;
    [period enumerateChildren:^(DXMLEventObject *child, BOOL *stop) {
        if ([[child elementName] isEqualToString:@"time-out"] || [[child elementName] isEqualToString:@"official-review"]) {
            if (child.timestamp) {
                retval = child;
            } else if (retval == nil) {
                retval = child;
            }
        }
    }];
    return retval;
}
@synthesize boutState = _boutState;
- (NSInteger) boutState
{
    return _boutState;
}
- (void) setBoutState:(NSInteger)boutState
{
    if (boutState == _boutState)
        return;
    NSInteger oldState = _boutState;
    _boutState = boutState;
    // change timers
    if (self.shouldPeriodClockRun) {
        if (oldState == DerbyBoutStatePending || oldState == DerbyBoutStateIntermission) {
            [self.periodTimer start];
        } else {
            [self.periodTimer resume];
        }
    } else {
        [self.periodTimer stop];
    }
    if (self.shouldJamClockRun) {
        if (!DerbyBoutStateShouldJamClockRun(oldState))
            [self.jamTimer start];
    } else {
        [self.jamTimer stop];
    }
    if (self.shouldLineupClockRun) {
        if (!DerbyBoutStateShouldLineupClockRun(oldState))
            [self.lineupTimer start];
    } else {
        [self.lineupTimer stop];
    }
    if (self.shouldTimeoutClockRun) {
        if (!DerbyBoutStateShouldTimeoutClockRun(oldState))
            [self.timeoutTimer start];
    } else {
        [self.timeoutTimer stop];
    }
    if (self.shouldIntermissionClockRun) {
        if (!DerbyBoutStateShouldIntermissionClockRun(oldState))
            [self.intermissionTimer start];
    } else {
        [self.intermissionTimer stop];
    }
}
- (void) updateBoutState: (DXMLObject *) xml
{
#ifdef nomre
    if ([xml.elementName isEqualToString:@"period"]) {
        [self updatePeriods];
    } else if ([xml.elementName isEqualToString:@"jam"]) {
        [self updateJam];
    } else if ([xml.elementName isEqualToString:@"time-out"] || [xml.elementName isEqualToString:@"official-review"]) {
        [self updateTimeout];
    } else
    {
        NSMutableDictionary *totals = [NSMutableDictionary dictionary];
        totals[[self.derbyXML.teams[0] attributeForKey: @"id"]] = @(0);
        totals[[self.derbyXML.teams[1] attributeForKey: @"id"]] = @(0);
        for (DXMLPeriodObject *period in self.derbyXML.periods) {
            for (DXMLJamObject *jam in period.jams) {
                [jam enumerateChildren:^(DXMLEventObject *child, BOOL *stop) {
                    id teamId = [child attributeForKey:@"id"];
                    NSInteger pts = [totals[teamId] integerValue];
                    pts += [[child attributeForKey:@"points"] integerValue];
                    totals[teamId] = @(pts);
                } ofClass:[DXMLPassObject class]];
            }
        }
    }
#endif
    [xml updateBoutState:self];
}
@end

@implementation DerbyBoutTeamState
- (id) initWithTeam:(DXMLTeamObject *)team
{
    self = [super init];
    if (self) {
        _team = team;
    }
    return self;
}
- (id) copyWithZone:(NSZone *)zone
{
    DerbyBoutTeamState *retval = [[DerbyBoutTeamState alloc] initWithTeam:self.team];
    return retval;
}
@end

@implementation DXMLObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state
{
}
- (void) updateBoutState: (DerbyBoutState *) state team:(DerbyBoutTeamState *)team
{
}
@end


@implementation DXMLJamObject(BoutState)
- (void) updateBoutState:(DerbyBoutState *)state
{
    [state updateJam];
    DXMLTimestamp *ts = self.timestamp;
    if (ts) {
        if ([self attributeForKey:@"duration"])
            state.boutState = DerbyBoutStateLineup;
        else
            state.boutState = DerbyBoutStateJamIsOn;
    }
}
@end

@implementation DXMLTimeOutObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state
{
    DXMLTeamObject *team = self.team;
    if (state.homeTeamState.team == team) {
        [self updateBoutState:state team:state.homeTeamState];
    } else if (state.awayTeamState.team == team) {
        [self updateBoutState:state team:state.awayTeamState];
    } else {
        if ([state updateTimeout] == self) {
            state.boutState = DerbyBoutStateOfficialTimeout;
        }
    }
}
- (void) updateBoutState: (DerbyBoutState *) state team: (DerbyBoutTeamState *) team;
{
    if ([state updateTimeout] == self) {
        state.boutState = DerbyBoutStateTeamTimeout;
    }
    team.timeoutsUsed += 1;
}
@end
@implementation DXMLOfficialReviewObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state team: (DerbyBoutTeamState *) team;
{
    if ([state updateTimeout] == self) {
        state.boutState = DerbyBoutStateOfficialReviewTimeout;
    }
    team.officalReviewsUsed += 1;
}
@end


@implementation DXMLBoutObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state
{
    // update based on periods
    for (DXMLPeriodObject *period in self.periods) {
//        NSInteger retval = [period updateBoutState:state];
//        if (retval != DerbyBoutStatePreBout && retval != DerbyBoutStatePending && retval != DerbyBoutStateIntermission)
//            return retval;
        state.homeTeamState.officalReviewsUsed = 0;
        state.awayTeamState.officalReviewsUsed = 0;
        [period updateBoutState:state];
    }
//    return state.boutState;
}
@end

@implementation DXMLPeriodObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state
{
    [state updatePeriods];
    DXMLTimestamp *ts = self.timestamp;
    if (ts) {
        if ([NSDate timeIntervalSinceReferenceDate] < ts.date.timeIntervalSinceReferenceDate) {
            // hasn't started yet
            if (self.bout.periods[0] == self) {
                // if first period hasn't started
                state.boutState = DerbyBoutStatePreBout;
            } else {
                state.boutState = DerbyBoutStateIntermission;
            }
        }
        state.boutState = DerbyBoutStatePending; // but this may change quickly
        for (DXMLSkaterEventObject *event in self.children) {
            [event updateBoutState:state];
        }
    }
}
@end


@implementation DXMLSkaterEventObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state
{
    DXMLTeamObject *team = self.team;
    if (state.homeTeamState.team == team) {
        [self updateBoutState:state team:state.homeTeamState];
    } else if (state.awayTeamState.team == team) {
        [self updateBoutState:state team:state.awayTeamState];
    }
}
@end
@implementation DXMLPassObject(BoutState)
- (void) updateBoutState: (DerbyBoutState *) state team: (DerbyBoutTeamState *) team;
{
    team.score += self.score;
}
@end
